//
//  Lattice.h
//  phi_four
//
//  Created by Snir Gazit on 3/8/12.
//  Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//

#ifndef Lattice_h
#define Lattice_h

#include "../HelpLibs/ArrayMultiD.h"
#include "../HelpLibs/def.h"

using namespace std;
using namespace boost;

typedef boost::array<int, DIM> IndexType;

enum MoveDir {
	LEFT = 0, RIGHT = 1
};

enum CreateRemove {
	CREATE = 0, REMOVE = 1
};

enum SectorType {
	Z = 0, GXY = 1, GISING = 2
};
template<int N>
struct Bond {
//	int dir;
	array<int, N> bondVal;
	friend class serialization::access;
	template<class Archive>
	void serialize(Archive & ar, const unsigned int version) {
		ar & bondVal;
		//	ar & dir;
	}
};

struct IsingBondType {
	//int dir;
	int bondVal;
	friend class serialization::access;
	template<class Archive>
	void serialize(Archive & ar, const unsigned int version) {
		ar & bondVal;
		//ar & dir;
	}
};
typedef Bond<2> XYBondType;
//typedef Bond<1> IsingBondType;

struct Site {
	typedef ArrayMulti<Site, DIM> SiteArray;
	typedef SiteArray::iterator SiteArrayIterator;
	typedef SiteArray::IterIndex SiteArrayIndexIterator;
	struct XYBondData {
		XYBondType * bond_;
		SiteArrayIterator neighbourSite_;
		MoveDir moveDir_;
		CreateRemove createRemove_;
		XYBondData(XYBondType *bond, SiteArrayIterator neighboursSite,
				MoveDir moveDir, CreateRemove createRemove) :
				bond_(bond), neighbourSite_(neighboursSite), moveDir_(moveDir), createRemove_(
						createRemove) {
		}
		XYBondData(XYBondData const &bondData) :
				bond_(bondData.bond_), neighbourSite_(bondData.neighbourSite_), moveDir_(
						bondData.moveDir_), createRemove_(
						bondData.createRemove_) {
		}
		int& getBondVal() {
			return bond_->bondVal[moveDir_];
		}
		static SectorType getSectorType() {
			return GXY;
		}
		static int movesPerDim() {
			return 4;
		}
		static SectorType getOtherSectorType() {
			return GISING;
		}
		static double gFactor() {
			return 0.5;
		}
		static double addRatio(int nab, int nc) {
			return double(2 * (1 + nab)) / double(3 + 2 * (nc + nab));
		}
		static double removeRatio(int nab, int nc) {
			return double(1 + 2 * (nc + nab)) / double(2 * nab);
		}
		static double addRatioOther(int nab, int nc) {
			return double((1 + 2 * (nc))) / double(3 + 2 * (nc + nab));
		}
		static double removeRatioOther(int nab, int nc) {
			return double(3 + 2 * (nc + nab - 1)) / double(1 + 2 * (nc - 1));
		}
	};
	struct IsingBondData {
		IsingBondType * bond_;
		SiteArrayIterator neighbourSite_;
		CreateRemove createRemove_;
		IsingBondData(IsingBondType *bond, SiteArrayIterator neighboursSite,
				CreateRemove createRemove) :
				bond_(bond), neighbourSite_(neighboursSite), createRemove_(
						createRemove) {
		}
		IsingBondData(IsingBondData const &bondData) :
				bond_(bondData.bond_), neighbourSite_(bondData.neighbourSite_), createRemove_(
						bondData.createRemove_) {
		}
		int& getBondVal() {
			return bond_->bondVal;
		}
		static SectorType getSectorType() {
			return GISING;
		}
		static SectorType getOtherSectorType() {
			return GXY;
		}
		static int movesPerDim() {
			return 4;
		}
		static double gFactor() {
			return 1.;
		}
		static double addRatio(int nc, int nab) {
			return double(1 + 2 * nc) / double(3 + 2 * (nc + nab));
		}
		static double removeRatio(int nc, int nab) {
			return double(1 + 2 * (nc + nab)) / double(2 * nc - 1);
		}
		static double addRatioOther(int nc, int nab) {
			return double(2 * (1 + nab)) / double(3 + 2 * (nc + nab));
		}
		static double removeRatioOther(int nc, int nab) {
			return double(3 + 2 * (nc + nab - 1)) / double(2 * (nab));
		}
	};
	typedef vector<XYBondData> XYBondArray;
	typedef vector<IsingBondData> IsingBondArray;
	XYBondArray XYBondArray_;
	IsingBondArray IsingBondArray_;
	//IndexType siteIndex_;
	int XYTotalBonds_;
	int IsingTotalBonds_;

	int getTotalBondsAll() {
		return XYTotalBonds_ + IsingTotalBonds_;
	}
	template<class BondData>
	int& getTotalBonds();

	template<class BondData>
	vector<BondData>& getBondArray();

	template<class BondData>
	int& getTotalBondsOther();

	friend class serialization::access;
	template<class Archive>
	void serialize(Archive & ar, const unsigned int version) {
		ar & XYTotalBonds_;
		ar & IsingTotalBonds_;
	}
};

class Lattice {
public:
	// Typedefs
	typedef Site::XYBondData XYBondData;
	typedef vector<XYBondData> XYBondDataVector;
	typedef XYBondDataVector::iterator XYBondDataVectorIterator;
	typedef Site::IsingBondData IsingBondData;
	typedef vector<IsingBondData> IsingBondDataVector;
	typedef IsingBondDataVector::iterator IsingBondDataVectorIterator;
	typedef boost::array<int, DIM> IndexType;
	typedef Site::SiteArrayIterator SiteIter;
	typedef Site::SiteArrayIndexIterator SiteIterIndex;
	typedef ArrayMulti<vector<XYBondType>, DIM> XYBondArray;
	typedef ArrayMulti<vector<IsingBondType>, DIM> IsingBondArray;
	typedef XYBondArray::iterator XYBondArrayIter;
	typedef IsingBondArray::iterator IsingBondArrayIter;
	// functions
	void init(boost::array<int, DIM> const &latSize);
	void initSquare(boost::array<int, DIM> const &latSize);
	SiteIter getIra();
	SiteIter getMasha();
	void setIra(Lattice::SiteIter &iter);
	void setMasha(Lattice::SiteIter &iter);
	SiteIter getSite(int distanceFromStart);
	boost::array<int, DIM> getSizes();
	bool isClosed() const;
	void moveIraMasha(IndexType moveToPos);
	int getCordinationNumber() const;
	int getNumOfSites() const;
	// Debug Functions
	bool testConfiguration(int &totalBonds);
	// Measure Functions
	XYBondArrayIter XYBondBegin();
	XYBondArrayIter XYBondEnd();
	IsingBondArrayIter IsingBondBegin();
	IsingBondArrayIter IsingBondEnd();
	SiteIter siteBegin();
	SiteIter siteEnd();
	SiteIterIndex siteBeginIndex();
	SiteIterIndex siteEndIndex();
	SectorType getSectorType() const;
	void setSectorType(SectorType sectorType);
	int getTotalBonds() const;
	void setToalBonds(int totalBonds);
	void dump(string fileName, bool final, ofstream *outFile);
	void read(string fileName);
	template<class Archive>
	void save(Archive & ar, const unsigned int version) const;
	template<class Archive>
	void load(Archive & ar, const unsigned int version);BOOST_SERIALIZATION_SPLIT_MEMBER()private:
	Site::SiteArray siteLattice_;
	XYBondArray XYBondLattice_;
	IsingBondArray IsingBondLattice_;
	SiteIter ira_, masha_;
	boost::array<int, DIM> latSize_;
	int cordinationNumber_;
	SectorType sectorType_;
	int totalBonds_;
};
template<class Archive>
void Lattice::save(Archive & ar, const unsigned int version) const {
	for (auto itb = XYBondLattice_.begin(); itb != XYBondLattice_.end(); itb++)
		for (auto itd = itb->begin(); itd != itb->end(); itd++)
			ar & *itd;
	for (auto itb = IsingBondLattice_.begin(); itb != IsingBondLattice_.end();
			itb++)
		for (auto itd = itb->begin(); itd != itb->end(); itd++)
			ar & *itd;
	for (auto itb = siteLattice_.begin(); itb != siteLattice_.end(); itb++)
		ar & *itb;
	int ira = ira_ - siteLattice_.begin();
	ar & ira;
	int masha = masha_ - siteLattice_.begin();
	ar & masha;
	ar & sectorType_;
	ar & totalBonds_;
}
template<class Archive>
void Lattice::load(Archive & ar, const unsigned int version) {
	for (auto itb = XYBondLattice_.begin(); itb != XYBondLattice_.end(); itb++)
		for (auto itd = itb->begin(); itd != itb->end(); itd++)
			ar & *itd;
	for (auto itb = IsingBondLattice_.begin(); itb != IsingBondLattice_.end();
			itb++)
		for (auto itd = itb->begin(); itd != itb->end(); itd++)
			ar & *itd;
	for (auto itb = siteLattice_.begin(); itb != siteLattice_.end(); itb++)
		ar & *itb;
	int ira;
	ar & ira;
	ira_ = this->siteBegin() + ira;
	int masha;
	ar & masha;
	masha_ = this->siteBegin() + masha;
	ar & sectorType_;
	ar & totalBonds_;
}
#endif
